﻿using System.Diagnostics;
using System.Text.RegularExpressions;

namespace zijian666.Data;

/// <summary>
/// 用于格式化 SQL 语句
/// </summary>
[DebuggerDisplay("{ToString()}")]
public partial class FormattableSql : FormattableString
{
    // 匹配: 左大括号 + 一组连续的数字,  前面不能是 2个左大括号, 后面可以是 空白,逗号,冒号,右大括号, 取出数字部分
#if NET7_0_OR_GREATER
    [GeneratedRegex(@"(?<!\{)\{(\d+)(?=[\s,:\}])")]
    private static partial Regex FormatNumberRegex();
#else
    private static readonly Regex _formatRegex = new(@"(?<!\{)\{(\d+)(?=[\s,:\}])", RegexOptions.Compiled);
    private static Regex FormatNumberRegex() => _formatRegex;
#endif

    private string AddFormatNumber(string format)
    {
        if (string.IsNullOrWhiteSpace(format))
        {
            return format ?? "";
        }
        var addNumber = Arguments.Count;
        return FormatNumberRegex().Replace(format, match =>
        {
            var number = int.Parse(match.Groups[1].Value);
            return "{" + (number + addNumber);
        });
    }


    public static FormattableSql Create(FormattableString formattable)
        => formattable as FormattableSql ?? new(formattable);

    public List<object?> Arguments { get; }

    public StringBuilder FormatSql { get; }

    private FormattableSql(FormattableString formattable)
        : this()
    {
        if (formattable is FormattableSql sql)
        {
            FormatSql.Append(sql.FormatSql);
            Arguments.AddRange(sql.Arguments);
        }
        else
        {
            FormatSql.Append(formattable.Format);
            Arguments.AddRange(formattable.GetArguments());
        }
    }

    public FormattableSql(string? format = null, IEnumerable<object?>? args = null)
    {
        FormatSql = new();
        Arguments = new();
        if (format is not null)
        {
            FormatSql.Append(format);
        }
        if (args is not null)
        {
            foreach (var arg in args)
            {
                Arguments.Add(arg);
            }
        }
    }

    public override object? GetArgument(int index) => Arguments[index];

    public override object?[] GetArguments() => [.. Arguments];

    public override string ToString(IFormatProvider? formatProvider)
    {
        return string.Format(new DbParameterFormatProvider(), FormatSql.ToString(), Arguments.ToArray());
    }

    public override int ArgumentCount => Arguments.Count;

    public override string Format => FormatSql.ToString();


    public FormattableSql Prepend(FormattableString formattable)
    {
        FormatSql.Insert(0, AddFormatNumber(formattable.Format));
        if (formattable is FormattableSql sql)
        {
            Arguments.AddRange(sql.Arguments);
        }
        else
        {
            Arguments.AddRange(formattable.GetArguments());
        }
        return this;
    }

    public FormattableSql PrependFormat(string format, params object?[] args)
    {
        if (format is not null)
        {
            FormatSql.Insert(0, AddFormatNumber(format));
        }
        if (args is not null)
        {
            Arguments.AddRange(args);
        }
        return this;
    }

    public FormattableSql PrependString(string str)
    {
        if (str is not null)
        {
            FormatSql.Insert(0, str);
        }
        return this;
    }

    public FormattableSql Append(FormattableString formattable)
    {
        FormatSql.Append(AddFormatNumber(formattable.Format));
        if (formattable is FormattableSql sql)
        {
            Arguments.AddRange(sql.Arguments);
        }
        else
        {
            Arguments.AddRange(formattable.GetArguments());
        }
        return this;
    }

    public FormattableSql AppendString(string format)
    {
        if (format is not null)
        {
            FormatSql.Append(format);
        }
        return this;
    }
    public FormattableSql AppendFormat(string format, params object?[] args)
    {
        if (format is not null)
        {
            FormatSql.Append(AddFormatNumber(format));
        }
        if (args is not null)
        {
            Arguments.AddRange(args);
        }
        return this;
    }

    public FormattableSql Append(bool condition, FormattableString formattable)
    {
        if (condition)
        {
            Append(formattable);
        }
        return this;
    }

    public FormattableSql AppendFormat(bool condition, string format, params object?[] args)
    {
        if (condition)
        {
            AppendFormat(format, args);
        }
        return this;
    }

    public FormattableSql AppendString(bool condition, string format)
    {
        if (condition)
        {
            AppendString(format);
        }
        return this;
    }
    public static FormattableSql operator +(FormattableSql left, FormattableString right)
    {
        return new FormattableSql(left).Append(right);
    }

    public static FormattableSql operator +(FormattableString left, FormattableSql right)
    {
        return new FormattableSql(left).Append(right);
    }

    public static FormattableSql operator +(FormattableSql left, string right)
    {
        return new FormattableSql(left).AppendString(right);
    }

    public static FormattableSql operator +(string left, FormattableSql right)
    {
        return new FormattableSql(left).Append(right);
    }
}
